COMP3141 Software System Design and Implementation

COMP3141: Software System Design and Implementation

Term 2, 2022

Quiz (Week 7)

Table of Contents

1 Applicatives

1.1 Question 1

Which of the following type definitions are examples of Applicative?

  1. Maybe
  2. String
  3. (->) a for any a
  4. (,) a for any a
  5. [ ]
  6. Gen

As usual, String is not a type constructor, so cannot be an Applicative. Furthermore (,) a does not admit a law-abiding applicative instance either, as we cannot implement pure:

instance Applicative ((,) x) where
  pure :: a -> (x, a)
  pure a = (??? , a)

Of the other options, Gen, Maybe, (->) a and [ ]. The latter three can be implemented as follows:

instance Applicative Maybe where
  pure x = Just x
  Just f <*> Just x = Just (f x)
  _      <*> _      = Nothing 

instance Applicative ((->) x) where
  pure a = \x -> a
  (xf <*> xa) x = xf x (xa x)

instance Applicative [ ] where
  pure a = [ a ]
  [] <*> ys = []
  (f:fs) <*> xs = map f xs ++ (fs <*> xs) 

1.2 Question 2

Consider the following data type definition for a non-empty list in Haskell.

data NonEmptyList a = One a | Cons a (NonEmptyList a)

Which of the following are law-abiding Applicative instances for NonEmptyList?

  1. instance Applicative NonEmptyList where
      pure x = Cons x (pure x)
      (One f) <*> (One x) = One (f x)
      (One f) <*> (Cons x _) = One (f x)
      (Cons f _) <*> (One x) = One (f x)
      (Cons f fs) <*> (Cons x xs) = Cons (f x) (fs <*> xs)
    
  2. instance Applicative NonEmptyList where
      pure x = One x
      (One f) <*> (One x) = One (f x)
      (One f) <*> (Cons x _) = One (f x)
      (Cons f _) <*> (One x) = One (f x)
      (Cons f fs) <*> (Cons x xs) = Cons (f x) (fs <*> xs)
    
  3. instance Applicative NonEmptyList where
      pure x = One x
      One f <*> xs = fmap f xs 
      (Cons f fs) <*> xs = fmap f xs `append` (fs <*> xs)
        where
         append (One x) ys = Cons x ys
         append (Cons x xs) ys = Cons x (xs `append` ys)
    
  4. instance Applicative NonEmptyList where
      pure x = Cons x (pure x)
      One f <*> xs = fmap f xs 
      (Cons f fs) <*> xs = fmap f xs `append` (fs <*> xs)
        where
         append (One x) ys = Cons x ys
         append (Cons x xs) ys = Cons x (xs `append` ys)
    

Option 3 is analogous to the same Applicative instance we knows from regular lists, so is a valid Applicative instance. Options 2 and 4 don't obey the first Applicative law, pure id <*> v = v. Option 1 is also a valid applicative instance, analogous to the ZipList instance available in the Haskell standard library.

1.3 Question 3

Suppose I wanted to write a function pair, which takes two Applicative data structures and combines them in a tuple, of type:

pair :: (Applicative f) => f a -> f b -> f (a, b)

Select all correct implementations of pair.

  1. pair fa fb = pure fa <*> pure fb
    
  2. pair fa fb = pure (,) <*> pure fa <*> pure fb
    
  3. pair fa fb = pure (,) <*> fa <*> fb
    
  4. pair fa fb = fmap (,) fa <*> fb
    
  5. There is no way to implement this function.

Options 1 and 2 are not type correct.

Options 3 and 4 are both correct, and equivalent, as fmap f x = (pure f <*> x), if the Applicative and Functor instances are law-abiding.

2 Monads

2.1 Question 4

Which of the following type definitions are examples of Monad, or admit law-abiding Monad instances?

  1. Maybe
  2. String
  3. (->) a for any a
  4. (,) a for any a
  5. [ ]
  6. Gen

Monad instances can be written for Maybe, (->) a and [ ], and Gen is an abstract type that also implements Monad. The rest have:

instance Monad Maybe where
  Just x  >>= f = f x
  Nothing >>= f = Nothing

instance Monad ((->) x) where
  (xa >>= axb) = \x -> axb (xa x) x

instance Monad [ ] where
  xs >>= each = concatMap each xs

2.2 Question 5

We wish to write a function s of type [m a] -> m [a], for any monad m. It will unpack each given value of type m a and collect their results into a list. Which of the following is a correct implementation of this function?

  1. s :: Monad m => [m a] -> m [a]
    s [] = []
    s (a:as) = do
      x <- a
      xs <- s as
      return (x : xs)
    
  2. s :: Monad m => [m a] -> m [a]
    s [] = return []
    s (a:as) = do
      x <- a
      xs <- as
      return (x : xs)
    
  3. s :: Monad m => [m a] -> m [a]
    s [] = return []
    s (a:as) = do
      x <- a
      xs <- s as
      return (x : xs)
    
  4. s :: Monad m => [m a] -> m [a]
    s [] = return []
    s (a:as) = do
      a
      s as
      return (a : as)
    

Only answer 3 is type correct.

3 Functor, Monad, Applicative

3.1 Question 6

Which of the following are correct definitions of the usual <*> operation for the type [a]?

  1. fs <*> xs = do
      f <- fs
      x <- xs
      return (f x)
    
  2. fs <*> xs = [f x | f <- fs, x <- xs]
    
  3. fs <*> xs =
      fs >>= \f ->
      xs >>= \x ->
      [f x]
    
  4. fs <*> xs =
      fs >>= \f ->
      xs >>= \x ->
      f x
    

Answers 1, 2 and 3 are three different syntactic forms expressing the same operation. However, Answer 4 is not type correct.

3.2 Question 7

Recall that two functions are considered equal if they have the same output for every input. Select all the true statements below.

  1. Kleisli composition is not well-defined in the Maybe monad.
  2. Every monad m and every function f :: a -> m a satisfies the equality f <=< f = f.
  3. Every instance of Applicative has to be an instance of Monad as well.
  4. Every instance of Monad has to be an instance of Functor as well.

Answer 4 is correct. Answer 1 is not, since Kleisli composition is well-defined in every monad. Answer 2 is not correct (think about e.g. the list monad). Answer 3 is not correct either: while every Applicative has to be a Functor, they need not be instances of Monad.

3.3 Question 8

If a given unary type m is both a Monad instance and a Functor instance, which of the following would definitely be correct implementations of fmap :: (a -> b) -> m a -> m b?

  1. fmap f [] = []
    fmap f (x:xs) = map f xs 
    
  2. fmap f = (<$>) f
    
  3. fmap f xs = f <*> xs
    
  4. fmap f xs =
      xs >>= \x -> return (f x) 
    

The implementations 1 and 3 do not (or may not) type-check. The others are correct.

Submission is already closed for this quiz. You can click here to check your submission (if any).

2022-09-06 Tue 00:54

Announcements RSS